//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

parse.cpp

Abstract:


Notes:


--*/
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author            Activity ID            Activity Headline
    2006-03-16          Wangxiaogang      CEDB00010042           W3K RIL Porting                
    2007-02-08          qinxuetao         CEDB200043651          Update ril driver for network and data service
    2007-04-24          hanjianjun         WM600002764            SIM contracts name missed when copy from handset.
------------------------------------------------------------------------------
*/
#include "precomp.h"

BOOL EncodedStringSizeToUnicodeStringSize(ENCODING_TYPE enc, size_t cbIn, size_t &cbOut)
{
    BOOL bResult = TRUE;
    switch (enc)
    {
    case ENCODING_GSMDEFAULT:
        // In GSM mode, 1 byte of data maps to 2-bytes (1 char) of unicode
        cbOut = cbIn<<1;
        break;

    case ENCODING_GSMDEFAULT_HEX:
        // In hex mode, 2 bytes of hex data map to 2-bytes (1 char) of unicode
        cbOut = cbIn;
        break;

    case ENCODING_GSMDEFAULT_UNICODE:
        // in unicode mode, 4 chars map to 2-bytes (1 char) of unicode
        cbOut = ( cbIn / 2 );
        break;

    case ENCODING_GSMDEFAULT_UTF8:
        // in utf-8 mode, 1 char map to at most 2-bytes (1 char) of unicode
        cbOut = cbIn<<1;
        break;

    default:
        bResult = FALSE;
    }

    return bResult;
}

//
// Determine if a string starts with another
//
BOOL MatchStringBeginning(const LPCSTR szString, const LPCSTR szMatch, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(MatchStringBeginning);
    BOOL fRet = FALSE;

    LPSTR szTemp = strstr(szString, szMatch);
    if (!szTemp)
    {
        goto End;
    }

    fRet = (szTemp == szString);
    if (fRet)
    {
        rszPointer = szString + strlen(szMatch);
#ifdef EMP_DRIVER
        // On EMP platform, more space characters may occur
        while (' ' == *rszPointer)
        {
            rszPointer++;
        }
#endif
    }

    End:
    return fRet;
}


//
// Determine if a string matches the end of another
//
BOOL MatchStringEnd(const LPCSTR szString, const LPCSTR szMatch, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(MatchStringEnd);
    UINT cbString = strlen(szString);
    UINT cbMatch = strlen(szMatch);
    BOOL fRet = FALSE;

    if (cbString >= cbMatch)
    {
        fRet = !strcmp(szString + cbString - cbMatch, szMatch);
    }
    if (fRet)
    {
        rszPointer = szString + cbString;
        DEBUGCHK('\0' == *rszPointer);
    }
    return fRet;
}



//
// Determine if a string contains another
//
BOOL MatchStringAnywhere(const LPCSTR szString, const LPCSTR szMatch, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(MatchStringAnywhere);
    BOOL fRet = FALSE;

    LPSTR szTemp = strstr(szString, szMatch);
    if (!szTemp)
    {
        goto End;
    }

    rszPointer = szTemp + strlen(szMatch);
    fRet = TRUE;

    End:
    return fRet;
}


//
// Determine if an array of bytes matches the end of another
//
BOOL MatchBytesEnd(const BYTE* const pbBytes, const UINT cbBytes, const BYTE* const pbMatch, const UINT cbMatch,
                   const BYTE*& rpbPointer)
{
    // FUNCTION_TRACE(MatchBytesEnd);
    BOOL fRet = FALSE;

    if (cbBytes >= cbMatch)
    {
        fRet = !memcmp(pbBytes + cbBytes - cbMatch, pbMatch, cbMatch);
    }
    if (fRet)
    {
        rpbPointer = pbBytes + cbBytes;
    }
    return fRet;
}



//
// Determine if an array of bytes contains another
//
BOOL MatchBytesAnywhere(const BYTE* const pbBytes, const UINT cbBytes, const BYTE* const pbMatch, const UINT cbMatch,
                        const BYTE*& rpbPointer)
{
    // FUNCTION_TRACE(MatchBytesAnywhere);
    const BYTE* pbWalk = pbBytes;
    const BYTE* pbEnd = pbBytes + cbBytes;
    BOOL fRet = FALSE;

    while (pbWalk < pbEnd)
    {
        if (!memcmp(pbWalk, pbMatch, cbMatch))
        {
            rpbPointer = pbWalk + cbMatch;
            fRet = TRUE;
            break;
        }

        pbWalk++;
    }
    return fRet;
}
/*===========================================================================
	FUNCTION: MatchStringBeginningAndAcorrdingParamentNumber 

	DESCRIPTION:
		match string beginning and according parament number,if all is same,we return true	
		
	PARAMETERS:
		OUT rszPointer :if find,then point will skip the head
		IN:szString,the source string
		szMatch:match string
		paramentNumber:parament number

	RETURN VALUE

===========================================================================*/
BOOL MatchStringBeginningAndAcorrdingParamentNumber(const LPSTR szString, const LPSTR szMatch,DWORD paramentNumber, LPSTR& rszPointer)
{
    BOOL fRet = FALSE;
    BOOL find = FALSE;
    DWORD Temp = 0;
    DWORD i = 0;
    DWORD TempParaNumber = 0;
    LPSTR  SzCR=NULL;

    LPSTR szTemp = strstr(szString, szMatch);

    if (!szTemp)
    {
        goto End;
    }

    find = (szTemp == szString);
    if (find)//find begin
    {
        szTemp = szString + strlen(szMatch);
	SzCR = strstr(szTemp, "\r\n");
   	if(SzCR > szTemp)
   	{
   		Temp = SzCR - szTemp;
		for( i= 0 ; i < Temp ;i++)
		{
			if(*( szTemp+i ) ==',')
				TempParaNumber++;
		}
		TempParaNumber ++;

		fRet=(TempParaNumber ==paramentNumber );

		if( fRet )
		{
			rszPointer = szString + strlen(szMatch);
		}

   	}
    }
    End:
    return fRet;
}

BOOL ParseDWord(const LPCSTR szData, const BOOL fLeadingZerosAllowed, DWORD& rdw, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseDWord);
    int nLeadingDigit = -1;
    BOOL Found=FALSE;

    // Skip any spaces
    SkipChars(szData, rszPointer, " ");

    rdw = 0;

    while (*rszPointer >= '0' && *rszPointer <= '9')
    {
        if (!fLeadingZerosAllowed)
        {
            // Test for leading 0s
            if (-1 == nLeadingDigit)
            {
                // Leading digit hasn't been set yet -- set it now
                nLeadingDigit = *rszPointer - '0';
            }
            else if (!nLeadingDigit)
            {
                // Leading digit is 0 and we got another digit
                // This means that we have leading 0s -- reset the pointer and punt
                Found=FALSE;
                goto Error;
            }
        }

        rdw = rdw * 10 + (*rszPointer - '0');
        rszPointer++;
        Found=TRUE;
    }

    // Skip past any spaces
    SkipChars(rszPointer, rszPointer, " ");

    Error:
    if (!Found)
    {
        rszPointer = szData;
    }
    return Found;
}


BOOL ParseHexDWord(const LPCSTR szData, const BOOL fLeadingZerosAllowed, DWORD& rdw, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseHexDWord);
    int nLeadingDigit = -1;
    UINT nDigit;

    rszPointer = szData;
    rdw = 0;

    while ((*rszPointer >= '0' && *rszPointer <= '9') ||
           (*rszPointer >= 'A' && *rszPointer <= 'F') ||
           (*rszPointer >= 'a' && *rszPointer <= 'f'))
    {
        if (!fLeadingZerosAllowed)
        {
            // Test for leading 0s
            if (-1 == nLeadingDigit)
            {
                // Leading digit hasn't been set yet -- set it now
                nLeadingDigit = *rszPointer - '0';
            }
            else if (!nLeadingDigit)
            {
                // Leading digit is 0 and we got another digit
                // This means that we have leading 0s -- reset the pointer and punt
                rszPointer = szData;
                goto Error;
            }
        }

        // Calculate the digit value
        if (*rszPointer >= 'A' && *rszPointer <= 'F')
        {
            nDigit = 10 + *rszPointer - 'A';
        }
        else if (*rszPointer >= 'a' && *rszPointer <= 'f')
        {
            nDigit = 10 + *rszPointer - 'a';
        }
        else
        {
            nDigit = *rszPointer - '0';
        }

        rdw = rdw * 16 + nDigit;
        rszPointer++;
    }

    Error:
    return (rszPointer > szData);
}

//
//
//
BOOL ParseUInt(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT& rnInt, LPCSTR& rszPointer)
{
    return ParseDWord(szData, fLeadingZerosAllowed, (DWORD&)rnInt, rszPointer);
}

//
//
//
BOOL ParseIntAsUInt(const LPCSTR szData, UINT& rnInit, LPCSTR& rszPointer)
{
    BOOL fNegative = FALSE;
    
    rszPointer = szData;

    if (*rszPointer == '-')
    {
        fNegative = TRUE;
        ++rszPointer;
    }

    if (!ParseUInt(rszPointer, FALSE, rnInit, rszPointer))
        return FALSE;

    if (fNegative)
    {
        rnInit = ~rnInit + 1;
    }

    return TRUE;
}

//
//
//
BOOL ParseUIntAndVerifyAbove(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT nUpperBound, UINT& rnInt, LPCSTR& rszPointer)
{
    BOOL fRet;

    // Parse the number
    fRet = ParseUInt(szData, fLeadingZerosAllowed, rnInt, rszPointer);

    // Verify that the number is greater than the upper bound
    if (fRet && rnInt >= nUpperBound)
    {
        fRet = FALSE;
        rszPointer = szData;
    }
    return fRet;
}


//
//
//
BOOL ParseUIntAndVerifyBelow(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT nLowerBound, UINT& rnInt, LPCSTR& rszPointer)
{
    BOOL fRet;

    // Parse the number
    fRet = ParseUInt(szData, fLeadingZerosAllowed, rnInt, rszPointer);

    // Verify that the number is less than the lower bound
    if (fRet && rnInt <= nLowerBound)
    {
        fRet = FALSE;
        rszPointer = szData;
    }
    return fRet;
}


//
//
//
BOOL ParseHexUInt(const LPCSTR szData, const BOOL fLeadingZerosAllowed, UINT& rnInt, LPCSTR& rszPointer)
{
    return ParseHexDWord(szData, fLeadingZerosAllowed, (DWORD&)rnInt, rszPointer);
}

BOOL ParseFixedPoint(const LPCSTR szData, DWORD &rdwFixedPoint, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseDouble);
    UINT dwIntPart;
    UINT dwFracPart;
    LPCSTR szFracStart;
    rszPointer = szData;

    // Parse the integer part
    if (!ParseUInt(rszPointer, TRUE, dwIntPart, rszPointer))
    {
        goto Error;
    }

    // Shift up by 16 bits
    dwIntPart <<= 16;

    // Parse decimal separator ("." -- this is what GSM uses)
    if ('.' != *rszPointer)
    {
        goto Error;
    }

    // Advance pointer past decimal point
    rszPointer++;

    // Parse the fractional part
    szFracStart=rszPointer;
    if (!ParseUInt(rszPointer, TRUE, dwFracPart, rszPointer))
    {
        goto Error;
    }

    // Shift up by 16 bits
    dwFracPart <<= 16;

    // Now normalize to lie in the lowest 16 bits.
    UINT dwDivisor;
    dwDivisor = 1;
    while (szFracStart<rszPointer)
    {
        dwDivisor*=10;
        szFracStart++;
    }
    dwFracPart/=dwDivisor;

    rdwFixedPoint = dwIntPart+dwFracPart;

    Error:
    return (rszPointer > szData);
}

#if 0 // Not currently used
//
// NOTE: parsing code for the decimal part is imprecise;
//       if higher precision needed, re-write this code.
//
BOOL ParseDouble(const LPCSTR szData, double& rdbDouble, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseDouble);
    UINT nIntPart;
    double dbDivisor;

    rszPointer = szData;
    rdbDouble = 0.0;

    // Parse the integer part
    if (!ParseUInt(rszPointer, TRUE, nIntPart, rszPointer))
    {
        goto Error;
    }
    rdbDouble = (double)nIntPart;

    // Parse decimal separator ("." -- this is what GSM uses)
    if ('.' != *rszPointer)
    {
        goto Error;
    }

    // Advance pointer past decimal point
    rszPointer++;

    // Parse decimal part
    dbDivisor = 10.0;

    while (*rszPointer >= '0' && *rszPointer <= '9')
    {
        rdbDouble += (*rszPointer - '0') / dbDivisor;
        dbDivisor *= 10;
        rszPointer++;
    }

    Error:
    return (rszPointer > szData);
}
#endif

//
//
//
BOOL ParseString(const LPCSTR szData, const LPSTR szOut, const UINT cbOut, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseString);
    LPSTR pchOut;
    LPSTR szOutEnd;
    BOOL fRet = FALSE;

    rszPointer = szData;
    pchOut = szOut;
    szOutEnd = szOut + cbOut;

#ifdef EMP_DRIVER
    // On EMP platform, a quoted string embraced by brackets is also a valid string
    BOOL fBracketed = FALSE;

    if (*rszPointer == '(')
    {
        fBracketed = TRUE;
        rszPointer++;
    }
#endif

    // Skip over the leading quote
    if (*rszPointer != '\"')
    {
        goto Error;
    }
    rszPointer++;

    while (pchOut < szOutEnd - 1)
    {
        if (!*rszPointer)
        {
            // We hit the end of the string -- fail
            goto Error;
        }
        else if ('\"' == *rszPointer)
        {
            // Terminating quote -- skip over it and bail out
            rszPointer++;
            fRet = TRUE;
            break;
        }
        else
        {
            *pchOut++ = *rszPointer++;
        }
    }
    *pchOut = '\0';

#ifdef EMP_DRIVER
    // On EMP platform, a quoted string embraced by brackets is also a valid string
    if (fBracketed)
    {
        if (*rszPointer != ')')
        {
            fRet = FALSE;
            goto Error;
        }
        rszPointer++;
    }
#endif

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

#define CAST_CTOW(c) ((WCHAR)((BYTE)(c)))

BOOL ParseStringGSMW(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    // Handle empty string
    if (pszIn==pszInEnd)
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    BYTE c;
    c = *pszIn++;
    switch (c)
    {
    case 0x80:
        {
            // String data is Unicode bytes in UCS2 encoding
            BOOL fOdd = TRUE;
            while (pwszOut < pwszOutEnd)
            {
                if (pszIn==pszInEnd)
                {
                    // We don't want to fail if the string ends in a partial character.  Just
                    // truncate it.  This will overwrite a trailing 1/2 char with the NULL byte
                    *pwszOut = L'\0';
                    return TRUE;
                }

                c = *pszIn++;

                // Assemble the UCS2 character incrementally
                if (fOdd)
                {
                    *pwszOut = CAST_CTOW(c) << 8;
                }
                else
                {
                    *pwszOut |= CAST_CTOW(c);
                    //when there is 0xFFFF in the end of string,we will processing with end.
                    if((*pwszOut == 0xFFFF)&&(pszIn == pszInEnd))
                    {
                        *pwszOut = L'\0';
                        return TRUE;
                    }
                    pwszOut++;
                }
                fOdd = !fOdd;
            }
            return FALSE;
        }
    case 0x81:
    case 0x82:
        {
            // Make sure at least 3 bytes of data
            if ( (pszIn+3) > pszInEnd )
            {
                return FALSE;
            }

            // Second octet is # of characters in the string
            BYTE Length = (BYTE)*pszIn++;

            WCHAR UCSBase;
            if (c==0x81)
            {
                // Third octet is bits 14-7 of the base pointer
                UCSBase = CAST_CTOW(*pszIn++) << 7;
            }
            else
            {
                // Third & fourth octets are the base pointer
                UCSBase =  CAST_CTOW(*pszIn++) << 8;
                UCSBase += CAST_CTOW(*pszIn++);
            }

            if (pszIn>=pszInEnd)
            {
                return FALSE;
            }

            UINT uiOutputLength = (pwszOutEnd > pwszOut) ? (pwszOutEnd - pwszOut) : 0;
            UINT ruiCharactersUsed = 0;
            BOOL ConvertResult = FALSE;

            if ( uiOutputLength && Length)
            {
                ConvertResult = ConvertGSMMixedToUnicode(UCSBase, pszIn, Length, pwszOut, uiOutputLength, ruiCharactersUsed);
    
                if ( ConvertResult )
                {
                    if ( ruiCharactersUsed < uiOutputLength)
                    {
                        pwszOut[ruiCharactersUsed] = L'\0';
                    }
                    else
                    {
                        pwszOut[uiOutputLength] = L'\0';
                    }
                }
                else
                {
                    pwszOut[0] = L'\0';
                }
            }

            return ConvertResult;

        }
    default:
        {
            // Normal string data - similar to ParseString above
            while (pwszOut < pwszOutEnd)
            {
                GSMCharToUnicode(c, pwszOut);
                pwszOut++;

                if (pszIn==pszInEnd)
                {
                    *pwszOut = L'\0';
                    return TRUE;
                }

                c = *pszIn++;
            }
            return FALSE;
        }
    }

    return FALSE;
}

BOOL ParseStringHexW(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    BOOL bRet;

    // Handle empty string (must have at least two characters).
    if ( (pszIn+2) > pszInEnd )
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    UINT cbInTmp = ((pszInEnd-pszIn)>>1);

    LPSTR pszInTmp = (LPSTR)LocalAlloc(0,cbInTmp);
    if (!pszInTmp)
    {
        return FALSE;
    }

    bRet = GSMHexToGSM(pszIn, pszInEnd-pszIn, pszInTmp, cbInTmp, cbInTmp);
    if (!bRet)
    {
        goto Exit;
    }

    bRet = ParseStringGSMW(pszInTmp, pszInTmp+cbInTmp, pwszOut, pwszOutEnd);

Exit:
    LocalFree(pszInTmp);
    return bRet;
}

BOOL ParseStringUnicodeW(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    // Handle empty string
    if (pszIn==pszInEnd)
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    UINT cbLen = 0;
    BOOL fRet = ConvertToUnicode(ENCODING_GSMDEFAULT_UNICODE,
                                    pszIn, ( pszInEnd - pszIn ),
                                    pwszOut, ( pwszOutEnd - pwszOut ),
                                    cbLen);

    if ( fRet )
    {
        if ( cbLen < (UINT)( pwszOutEnd - pwszOut ) )
        {
            pwszOut[cbLen] = L'\0';
        }
        else
        {
            fRet = FALSE;
        }
    }

    return fRet;
}

BOOL ParseStringUTF8W(LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    // Handle empty string
    if (pszIn==pszInEnd)
    {
        *pwszOut = L'\0';
        return TRUE;
    }

    UINT cbLen = 0;
    BOOL fRet = ConvertToUnicode(ENCODING_GSMDEFAULT_UTF8,
                                    pszIn, ( pszInEnd - pszIn ),
                                    pwszOut, ( pwszOutEnd - pwszOut ),
                                    cbLen);

    if ( fRet )
    {
        if ( cbLen < (UINT)( pwszOutEnd - pwszOut ) )
        {
            pwszOut[cbLen] = L'\0';
        }
        else
        {
            fRet = FALSE;
        }
    }

    return fRet;
}

BOOL ParseEncodedString(ENCODING_TYPE enc, LPCSTR pszIn, const LPCSTR pszInEnd, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    switch (enc)
    {
    case ENCODING_GSMDEFAULT:
        return ParseStringGSMW(pszIn, pszInEnd, pwszOut, pwszOutEnd);

    case ENCODING_GSMDEFAULT_HEX:
        return ParseStringHexW(pszIn, pszInEnd, pwszOut, pwszOutEnd);

    case ENCODING_GSMDEFAULT_UNICODE:
        return ParseStringUnicodeW(pszIn, pszInEnd, pwszOut, pwszOutEnd);

    case ENCODING_GSMDEFAULT_UTF8:
        return ParseStringUTF8W(pszIn, pszInEnd, pwszOut, pwszOutEnd);
    }

    return FALSE;
}

BOOL ParseQuotedEncodedString(ENCODING_TYPE enc, LPCSTR& pszIn, LPWSTR pwszOut, const LPWSTR pwszOutEnd)
{
    LPCSTR pszInTmp = pszIn;

    // Skip over the leading quote
    DWORD c;
    c = *pszInTmp++;
    if (c != '\"')
    {
        return FALSE;
    }

    // Calculate string length
    LPCSTR pszInEnd = strchr(pszInTmp, '\"');
    if (!pszInEnd)
    {
        return FALSE;
    }

    if (!ParseEncodedString(enc, pszInTmp, pszInEnd, pwszOut, pwszOutEnd))
    {
        return FALSE;
    }

    pszIn = pszInEnd+1;
    return TRUE;
}

BOOL AppendStringUCS2W(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd)
{
    LPSTR pszOutTmp = pszOut;
    LPCWSTR pwszInTmp = pwszIn;
    WCHAR wc;

    if (pszOutTmp>=pszOutEnd)
    {
        return FALSE;
    }
    *pszOutTmp++ = (BYTE)0x80;

    // Iterate through all the characters in the buffer and store
    pwszInTmp = pwszIn;
    while (wc=*pwszInTmp++)
    {
        if (pszOutTmp>=pszOutEnd)
        {
            return FALSE;
        }
        *pszOutTmp++=(BYTE)(wc>>8);

        if (pszOutTmp>=pszOutEnd)
        {
            return FALSE;
        }
        *pszOutTmp++=(BYTE)(wc);
    }

    pszOut = pszOutTmp;
    return TRUE;
}

BOOL AppendStringGSMW(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode, BOOL bFilterGsmChars)
{
    LPSTR pszOutTmp = pszOut;
    LPCWSTR pwszInTmp = pwszIn;
    WCHAR wc;

    while (wc=*pwszInTmp++)
    {
        // If character can't be converted to GSM,
        // or if it's a control character (which can screw up the module)
        // or if it's a double-quote character (which will also screw up the module)
        // Then skip it (or switch to Unicode if allowed)
        char c;
        if ( (!UnicodeCharToGSM(wc, &c)) ||
             (bFilterGsmChars && ((c<32) || (c=='\"'))) )
        {
            if (bAllowUnicode)
            {
                // Append in unicode mode
                return AppendStringUCS2W(pwszIn,pszOut,pszOutEnd);
            }
            else
            {
                // Skip the character
                continue;
            }
        }

        if (pszOutTmp>=pszOutEnd)
        {
            return FALSE;
        }
        *pszOutTmp++=c;
    }

    pszOut = pszOutTmp;
    return TRUE;
}

BOOL AppendStringHexW(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    BOOL bRet;
    UINT cbIn, cbOut;

    // Allocate a buffer to hold the raw (non-hex) gsm characters
    cbIn = _tcslen(pwszIn);

    // If we might use unicode, we need enough space in the intermediate buffer to hold the Unicode format,
    // which takes 2 bytes per unicode char, plus 1 additional byte at the beginning.
    if (bAllowUnicode)
    {
        cbIn = (cbIn<<1) + 1;
    }

    // Handle empty string.
    if (cbIn==0)
    {
        return TRUE;
    }

    LPSTR pszIn = (LPSTR)LocalAlloc(0,cbIn);
    if (!pszIn)
    {
        return FALSE;
    }

    // Pass in a temp ptr, since this will update the ptr and we don't want to lose pszIn.
    LPSTR pszInTmp=pszIn;
    bRet = AppendStringGSMW(pwszIn, pszInTmp, pszInTmp + cbIn, bAllowUnicode, FALSE);
    if (!bRet)
    {
        goto Exit;
    }

    cbIn = pszInTmp-pszIn;
    cbOut = pszOutEnd-pszOut;
    bRet = GSMToGSMHex(pszIn, cbIn, pszOut, cbOut, cbOut);
    pszOut = pszOut + cbOut;

Exit:
    LocalFree(pszIn);
    return bRet;
}

BOOL AppendStringUnicodeW(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    UINT cbIn = _tcslen( pwszIn );
    UINT cbOut = ( pszOutEnd - pszOut );
    UINT cbLen = 0;

    BOOL fRet = ConvertFromUnicode( ENCODING_GSMDEFAULT_UNICODE, pwszIn, cbIn, pszOut, cbOut, cbLen );
    if ( fRet )
    {
        pszOut += cbLen;
        if ( cbLen < cbOut )
        {
            *pszOut = '\0';
        }
        else
        {
            fRet = FALSE;
        }
    }

    return fRet;
}

BOOL AppendStringUTF8W(LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    UINT cbIn = _tcslen( pwszIn );
    UINT cbOut = ( pszOutEnd - pszOut );
    UINT cbLen = 0;

    BOOL fRet = ConvertFromUnicode( ENCODING_GSMDEFAULT_UTF8, pwszIn, cbIn, pszOut, cbOut, cbLen );
    if ( fRet )
    {
        pszOut += cbLen;
        if ( cbLen < cbOut )
        {
            *pszOut = '\0';
        }
        else
        {
            fRet = FALSE;
        }
    }

    return fRet;
}

BOOL AppendEncodedString(ENCODING_TYPE enc, LPCWSTR pwszIn, LPSTR& pszOut, const LPSTR pszOutEnd, BOOL bAllowUnicode)
{
    switch (enc)
    {
    case ENCODING_GSMDEFAULT:
        return AppendStringGSMW(pwszIn,pszOut,pszOutEnd,bAllowUnicode,TRUE);

    case ENCODING_GSMDEFAULT_HEX:
        return AppendStringHexW(pwszIn,pszOut,pszOutEnd,bAllowUnicode);

    case ENCODING_GSMDEFAULT_UNICODE:
        return AppendStringUnicodeW(pwszIn,pszOut,pszOutEnd,bAllowUnicode);

    case ENCODING_GSMDEFAULT_UTF8:
        return AppendStringUTF8W(pwszIn,pszOut,pszOutEnd,bAllowUnicode);

    default:
        return FALSE;
    }

    return FALSE;
}

BOOL AppendQuotedEncodedString(ENCODING_TYPE enc, LPCWSTR pwszIn, LPSTR& pszOut, const PUCHAR pszOutEnd, BOOL bAllowUnicode)
{
    BOOL bResult;

    LPSTR pszOutTmp = pszOut;
    if (pszOutTmp>=(LPSTR)pszOutEnd)
    {
        return FALSE;
    }

    *pszOutTmp++='\"';
    if (pszOut>=(LPSTR)pszOutEnd)
    {
        return FALSE;
    }

    bResult = AppendEncodedString(enc,pwszIn,pszOutTmp,(LPSTR)pszOutEnd,bAllowUnicode);
    if (!bResult)
    {
        return FALSE;
    }

    *pszOutTmp++='\"';
    if (pszOutTmp>=(LPSTR)pszOutEnd)
    {
        return FALSE;
    }

    *pszOutTmp='\0'; // Put on a NULL just to be safe
    pszOut = pszOutTmp;
    return TRUE;
}

//
//
//
BOOL ParseUnquotedString(const LPCSTR szData, const char chDelimiter, const LPSTR szOut, const UINT cbOut, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseUnquotedString);
    LPSTR pchOut;
    LPSTR szOutEnd;
    BOOL fRet = FALSE;

    rszPointer = szData;
    pchOut = szOut;
    szOutEnd = szOut + cbOut;

    // Copy the string data until we encounter the delimiter character
    while (chDelimiter != *rszPointer && pchOut < szOutEnd - 1)
    {
        *pchOut++ = *rszPointer++;
    }
    *pchOut = '\0';

    // Skip forward until the delimiter character, if we haven't reached it yet
    if (chDelimiter != *rszPointer)
    {
        rszPointer = strchr(rszPointer, chDelimiter);
        if (!rszPointer)
        {
            // We didn't find the delimiter in the string -- fail
            goto Error;
        }
    }
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
// NOTE: caller is responsible for free'ing the storage used by rszOut
//
BOOL ParseUnlimitedString(const LPCSTR szData, LPSTR& rszOut, UINT& rcbOut, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseUnlimitedString);
    LPSTR pchTerminatingQuote;
    BOOL fRet = FALSE;

    rszOut = NULL;
    rszPointer = szData;

    // Skip over the leading quote
    if (*rszPointer != '\"')
    {
        goto Error;
    }
    rszPointer++;

    // Calculate string length
    pchTerminatingQuote = strchr(rszPointer, '\"');
    if (!pchTerminatingQuote)
    {
        goto Error;
    }
    rcbOut = pchTerminatingQuote - rszPointer + 1;

    // Allocate sufficient storage for the string
    rszOut = new char[rcbOut];
    if (!rszOut)
    {
        goto Error;
    }

    // Copy the string into the allocated buffer
    (void)strncpyz(rszOut, rszPointer, rcbOut);
    *(rszOut + rcbOut - 1) = '\0';
    rszPointer = pchTerminatingQuote + 1;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete[] rszOut;
        rszOut = NULL;
        rcbOut = 0;
        rszPointer = szData;
    }
    return fRet;
}

//
// NOTE: caller is responsible for free'ing the storage used by rszOut
//
BOOL ParseUnlimitedUnquotedString(const LPCSTR szData, const char chDelimiter, LPSTR& rszOut, UINT& rcbOut, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseUnlimitedUnquotedString);
    LPSTR pchTerminator;
    BOOL fRet = FALSE;

    rszOut = NULL;
    rszPointer = szData;

    // Calculate string length
    pchTerminator = strchr(rszPointer, chDelimiter);
    if (!pchTerminator)
    {
        goto Error;
    }
    rcbOut = pchTerminator - rszPointer + 1;

    // Allocate sufficient storage for the string
    rszOut = new char[rcbOut];
    if (!rszOut)
    {
        goto Error;
    }

    // Copy the string into the allocated buffer
    (void)strncpyz(rszOut, rszPointer, rcbOut);
    *(rszOut + rcbOut - 1) = '\0';
    rszPointer = pchTerminator;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete[] rszOut;
        rszOut = NULL;
        rcbOut = 0;
        rszPointer = szData;
    }
    return fRet;
}


//
//
//
BOOL ParseRange(const LPCSTR szData, RILRANGE& rrrRange, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseRange);
    UINT nValue;
    BOOL fRet = FALSE;

    rszPointer = szData;

    // Parse "("
    if ('(' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    // Parse "<min>"
    if (!ParseUInt(rszPointer, TRUE, nValue, rszPointer))
    {
        goto Error;
    }
    rrrRange.dwMinValue = nValue;

    // Try parsing ")" for ranges with a single value
    if (')' == *rszPointer)
    {
        rrrRange.dwMaxValue = nValue;
        rszPointer++;
        fRet = TRUE;
        goto Error; // ...although not really an error
    }

    // Parse "-"
    if ('-' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    // Parse "<max>"
    if (!ParseUInt(rszPointer, TRUE, nValue, rszPointer))
    {
        goto Error;
    }
    rrrRange.dwMaxValue = nValue;
    DEBUGCHK(rrrRange.dwMinValue <= rrrRange.dwMaxValue);

    // Parse ")"
    if (')' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
//
//
BOOL ParseRangesFromString(const LPCSTR szData,
                           RILRANGE rgrrRange[],
                           DWORD& rdwNumRanges,
                           DWORD dwMaxRanges,
                           LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseRanges);
    UINT nValue1;
    UINT nValue2;
    DWORD dwCurrentRange = 0;
    BOOL fRet = FALSE;

    rszPointer = szData;

    while (dwCurrentRange < dwMaxRanges)
    {
        // Parse the first value
        if (!ParseUInt(rszPointer, TRUE, nValue1, rszPointer))
        {
            goto Error;
        }

        // Parse "-"
        if (MatchStringBeginning(rszPointer, "-", rszPointer))
        {
            // Parse the second value
            if (!ParseUInt(rszPointer, TRUE, nValue2, rszPointer))
            {
                goto Error;
            }
        }
        else if (MatchStringBeginning(rszPointer, "x", rszPointer))
        {
            // The second digit of Value1 can be anything
            nValue1 *= 10;
            nValue2 = nValue1 + 9;
        }
        else
        {
            nValue2 = nValue1;
        }
        DEBUGCHK(nValue1 <= nValue2);

        rgrrRange[dwCurrentRange].dwMinValue = nValue1;
        rgrrRange[dwCurrentRange].dwMaxValue = nValue2;

        dwCurrentRange++;

        // Parse ","
        if (',' != *rszPointer)
        {
            break;
        }
        rszPointer++;
    }

    rdwNumRanges = dwCurrentRange;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareListValues(const void* pElem1, const void* pElem2)
{
    UINT n1 = ((LISTVALUEMAP*)pElem1)->nValue;
    UINT n2 = ((LISTVALUEMAP*)pElem2)->nValue;
    int iRet;

    if (n1 < n2)
    {
        iRet = -1;
    }
    else if (n1 == n2)
    {
        iRet = 0;
    }
    else
    {
        iRet = 1;
    }
    return iRet;
}

BOOL FlagFromValue(UINT nValue, const LISTVALUEMAP* const rglsmMapArray, const UINT nMapArrayLength, DWORD& Flag)
{
    LISTVALUEMAP lsmKey;
    LISTVALUEMAP* plsmFound;

    // Add correspondig flag to the mask
    lsmKey.nValue = nValue;
    plsmFound = (LISTVALUEMAP*)bsearch(&lsmKey, rglsmMapArray, nMapArrayLength, sizeof(LISTVALUEMAP), BSCompareListValues);
    if (plsmFound)
    {
        Flag = plsmFound->dwFlag;
        return TRUE;
    }
    else
    {
        Flag=0;
        return FALSE;
    }
}

//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareListStrings(const void* pElem1, const void* pElem2)
{
    LPCSTR sz1 = ((LISTSTRINGMAP*)pElem1)->szString;
    LPCSTR sz2 = ((LISTSTRINGMAP*)pElem2)->szString;
    return strcmp(sz1, sz2);
}

BOOL FlagFromString(const LPCSTR szString, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength, DWORD& Flag)
{
    LISTSTRINGMAP lsmKey;
    LISTSTRINGMAP* plsmFound;

    // Add correspondig flag to the mask
    lsmKey.szString = szString;
    plsmFound = (LISTSTRINGMAP*)bsearch(&lsmKey, rglsmMapArray, nMapArrayLength, sizeof(LISTSTRINGMAP), BSCompareListStrings);
    if (plsmFound)
    {
        Flag = plsmFound->dwFlag;
        return TRUE;
    }
    else
    {
        Flag=0;
        return FALSE;
    }
}

BOOL ValueFromFlag(UINT dwFlag, const LISTVALUEMAP* const rglsmMapArray, const UINT nMapArrayLength, UINT& Value)
{
    UINT i;
    for (i=0;i<nMapArrayLength;i++)
    {
        if (rglsmMapArray[i].dwFlag == dwFlag)
        {
            Value=rglsmMapArray[i].nValue;
            return TRUE;
        }
    }
    return FALSE;
}

BOOL StringFromFlag(UINT dwFlag, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength, LPCSTR& szString)
{
    UINT i;
    for (i=0;i<nMapArrayLength;i++)
    {
        if (rglsmMapArray[i].dwFlag == dwFlag)
        {
            szString=rglsmMapArray[i].szString;
            return TRUE;
        }
    }
    return FALSE;
}

//
//
//
BOOL ParseRangeList(const LPCSTR szData, const LISTVALUEMAP* const rglvmMapArray, const UINT nMapArrayLength, DWORD& rdwMask,
                    LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseRangeList);
    UINT nValue1;
    UINT nValue2;
    UINT i;
    BOOL fRet = FALSE;

    rdwMask = 0x00000000;
    rszPointer = szData;

    // Parse "("
    if ('(' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    while (1)
    {
        // Parse the first value
        if (!ParseUInt(rszPointer, TRUE, nValue1, rszPointer))
        {
            goto Error;
        }

        // Parse "-"
        if (MatchStringBeginning(rszPointer, "-", rszPointer))
        {
            // Parse the second value
            if (!ParseUInt(rszPointer, TRUE, nValue2, rszPointer))
            {
                goto Error;
            }
        }
        else if (MatchStringBeginning(rszPointer, "x", rszPointer))
        {
            // The second digit of Value1 can be anything
            nValue1 *= 10;
            nValue2 = nValue1 + 9;
        }
        else
        {
            nValue2 = nValue1;
        }
        DEBUGCHK(nValue1 <= nValue2);

        // Walk through this sub-range and add corresponding flags to the mask
        for (i = nValue1; i <= nValue2; i++)
        {
            DWORD Flag;
            if (FlagFromValue(i,rglvmMapArray,nMapArrayLength,Flag))
            {
                rdwMask |= Flag;
            }
        }

        // Parse ","
        if (',' != *rszPointer)
        {
            break;
        }
        rszPointer++;
    }

    // Parse ")"
    if (')' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}

//
//
//
BOOL ParseStringList(const LPCSTR szData, const LISTSTRINGMAP* const rglsmMapArray, const UINT nMapArrayLength, DWORD& rdwMask,
                     LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseStringList);
    char szString[MAX_PATH];
    BOOL fRet = FALSE;

    rdwMask = 0x00000000;
    rszPointer = szData;

    // Parse "("
    if ('(' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;

    while (1)
    {
        // Parse the next string
        if (!ParseString(rszPointer, szString, MAX_PATH, rszPointer))
        {
            goto Error;
        }

        // Add corresponding flag to the mask
        DWORD Flag;
        if (FlagFromString(szString, rglsmMapArray, nMapArrayLength,Flag))
        {
            rdwMask |= Flag;
        }

        // Parse ","
        if (',' != *rszPointer)
        {
            break;
        }
        rszPointer++;
    }

    // Parse ")"
    if (')' != *rszPointer)
    {
        goto Error;
    }
    rszPointer++;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        rszPointer = szData;
    }
    return fRet;
}


// Note that if ParsePartialResponse thinks there is a partial response, it will throw away
// the partial response up to the '+' character of the next response. This means that it will
// also throw away any leading <cr><lf> prefix on the next call. This works because
// ParseRspPrefix always returns TRUE, even if the <cr><lf> prefix isn't there. So, if you
// ever have ParseRspPrefix return FALSE if the prefix isn't there, go back into ParsePartialResponse
// and fix it to leave the prefix at the beginning of the next response.
//
//
//
BOOL ParseRspPrefix(const LPCSTR szData, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseRspPrefix);

    //(void)MatchStringBeginning(szData, "\r\n", rszPointer);
    LPSTR szTemp = (LPSTR)szData;
    while(MatchStringBeginning(szTemp, "\r\n", rszPointer))
    {
        szTemp = (LPSTR)rszPointer;
    }

    return TRUE;
}


//
//
//
BOOL ParseRspPostfix(const LPCSTR szData, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(ParseRspPostfix);
    BOOL fRet;

    fRet = MatchStringBeginning(szData, "\r\n", rszPointer);
#ifdef EMP_DRIVER
	// On EMP platform it is acceptable to have only a '\r' in the response
	if (!fRet)
	{
		fRet = MatchStringBeginning(szData, "\r", rszPointer);
	}
#endif

    return fRet;
}


//
//
//
BOOL FindRspPostfix(const LPCSTR szData, LPCSTR& rszPointer)
{
    // FUNCTION_TRACE(FindRspPostfix);
    BOOL fRet;

    fRet = MatchStringAnywhere(szData, "\r\n", rszPointer);
#ifdef EMP_DRIVER
	// On EMP platform it is acceptable to have only a '\r' in the response
	if (!fRet)
	{
		fRet = MatchStringAnywhere(szData, "\r", rszPointer);
	}
#endif

    return fRet;
}

